package seq;

import java.io.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicLong;

public class CertNOGenerator {

    private static final int SLOT = 1000;
    private static final int SEQ_BIT = 16;
    private static final long SEQ_MASK = (1 << SEQ_BIT) - 1;

    private final AtomicLong baseMills = new AtomicLong(0);

    private ConcurrentHashMap<Integer, AtomicLong> hashMap = new ConcurrentHashMap<>();

    public String nextNO() {
        long millis = System.currentTimeMillis();
        baseMills.compareAndSet(0L, millis);

        Integer key = (int) (millis % SLOT);
        long base = baseMills.get();

        long initialValue = (millis - base) << SEQ_BIT;
        AtomicLong atomicLong = hashMap.computeIfAbsent(key, k -> new AtomicLong(initialValue));
        long value = atomicLong.get();
        if (value >> SEQ_BIT != millis - base) {
            atomicLong.compareAndSet(value, initialValue);
        }
        value = atomicLong.get();
        long seq = value & SEQ_MASK;
        boolean less = seq < 99;
        if (less) {
            value = atomicLong.incrementAndGet();
        }
        seq = value & SEQ_MASK;

        if (seq > 99 || !less) {
            return sleepNextNO(0);
        }
        return ((value >> SEQ_BIT) + base) + "_" + seq;
    }

    private String sleepNextNO(long mills) {
        try {
            Thread.sleep(mills);
        } catch (Exception ignore) {

        }
        return nextNO();
    }

    private static final int tc = 2000;

    private static ExecutorService executorService = Executors.newFixedThreadPool(tc);


    public static void main(String[] args) throws FileNotFoundException, InterruptedException {

        CertNOGenerator certNOGenerator = new CertNOGenerator();


        ByteArrayOutputStream[] ss = new ByteArrayOutputStream[tc];
        CountDownLatch downLatch = new CountDownLatch(tc);

        for (int i = 0; i < tc; i++) {
            ByteArrayOutputStream si = new ByteArrayOutputStream(40000);
            ss[i] = si;
        }


        for (int i = 0; i < tc; i++) {
            PrintWriter out = new PrintWriter(ss[i]);
            executorService.submit(() -> {
                long end = System.currentTimeMillis() + 1000;
                while (System.currentTimeMillis() <= end) {
                    out.println(certNOGenerator.nextNO());
                }
                out.flush();
                downLatch.countDown();
            });
        }

        downLatch.await();
        executorService.shutdown();
        System.out.println(certNOGenerator.hashMap.size());

        PrintStream printStream = new PrintStream(new FileOutputStream("D:/nos.txt"));
        for (int i = 0; i < tc; i++) {
            printStream.print(ss[i].toString());
        }
        printStream.close();
    }

}
